package com.auto.tv.serial.comn;

import android.os.HandlerThread;
import android.serialport.SerialPort;
import android.util.Log;

import com.auto.tv.serial.comn.constant.FlagManager;
import com.auto.tv.serial.comn.message.LogManager;
import com.auto.tv.serial.comn.message.SendMessage;
import com.auto.tv.serial.comn.utils.CustomByteUtil;
import com.licheedev.hwutils.ByteUtil;
import com.licheedev.myutils.LogPlus;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;

/**
 * Created by Administrator on 2017/3/28 0028.
 */
public class SerialPortManager {

    private static final String TAG = "SerialPortManager";

    private SerialReadThread mReadThread;
    private OutputStream mOutputStream;
    private HandlerThread mWriteThread;
    private Scheduler mSendScheduler;
    private boolean isClose;

    private static class InstanceHolder {
        public static SerialPortManager sManager = new SerialPortManager();
    }

    public static SerialPortManager instance() {
        return InstanceHolder.sManager;
    }

    private SerialPort mSerialPort;

    private SerialPortManager() {
    }

    /**
     * 打开串口
     *
     * @param device
     * @return
     */
    public SerialPort open(Device device) {
        return open(device.getPath(), device.getBaudrate());
    }

    /**
     * 打开串口
     *
     * @param devicePath
     * @param baudrateString
     * @return
     */
    public SerialPort open(String devicePath, String baudrateString) {
        if (mSerialPort != null) {
            close();
        }

        try {
            File device = new File(devicePath);
            int baurate = Integer.parseInt(baudrateString);
            mSerialPort = new SerialPort(device, baurate);

            mReadThread = new SerialReadThread(mSerialPort.getInputStream());
            mReadThread.start();
            mOutputStream = mSerialPort.getOutputStream();
            mWriteThread = new HandlerThread("write-thread");
            mWriteThread.start();
            mSendScheduler = AndroidSchedulers.from(mWriteThread.getLooper());
            return mSerialPort;
        } catch (Throwable tr) {
            LogPlus.e(TAG, "打开串口失败", tr);
            close();
            return null;
        }
    }

    public boolean isClosed() {
        return isClose;
    }

    /**
     * 关闭串口
     */
    public void close() {
        isClose = true;
        if (mReadThread != null) {
            mReadThread.close();
            mReadThread = null;
        }
        if (mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (mWriteThread != null) {
            mWriteThread.quit();
            mWriteThread = null;
        }

        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
        }
    }

    /**
     * 发送数据
     *
     * @param datas
     * @return
     */
    private void sendData(byte[] datas) throws Exception {
        mOutputStream.write(datas);
    }

    /**
     * (rx包裹)发送数据
     *
     * @param datas
     * @return
     */
    private Observable<Object> rxSendData(final byte[] datas) {
        return Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                try {
                    sendData(datas);
                    emitter.onNext(new Object());
                } catch (Exception e) {
                    LogPlus.e("发送：" + ByteUtil.bytes2HexStr(datas) + " 失败", e);
                    if (!emitter.isDisposed()) {
                        emitter.onError(e);
                        return;
                    }
                }
                emitter.onComplete();
            }
        });
    }

    /**
     * 发送命令包
     */
    public void sendCommand(final String command) {
        LogPlus.i("发送命令：" + command);

        byte[] bytes = ByteUtil.hexStr2bytes(command);
        rxSendData(bytes).subscribeOn(mSendScheduler).subscribe(new Observer<Object>() {
            @Override
            public void onSubscribe(Disposable d) {
                
            }

            @Override
            public void onNext(Object o) {
                LogManager.instance().post(new SendMessage(command));
            }

            @Override
            public void onError(Throwable e) {
                LogPlus.e("发送失败", e);
            }

            @Override
            public void onComplete() {

            }
        });
    }

    public int serialNumber = 0;
    /**
     * 上位机下发命令
     *
     * @param code
     * @param data String数据
     */
    public void sendCommand(int code, String data) {
        String head = "11AA";
        int data_length = data.length() / 2;
        int num = this.serialNumber;
        this.serialNumber++;
        if (this.serialNumber > 255) {
            this.serialNumber = 0;
        }
        String code_hexstring = CustomByteUtil.byteToHex(code);
        String serialNumber_hexstring = CustomByteUtil.byteToHex(serialNumber);
        String data_length_hexstring = CustomByteUtil.byteToHex(data_length);
        String precheck = code_hexstring + serialNumber_hexstring + data_length_hexstring + data;
        byte[] precheck_array = CustomByteUtil.toByteArray(precheck);
        int check = CustomByteUtil.checkDigit(precheck_array);
        String hex_code = head + precheck + CustomByteUtil.byteToHex(check) + "55";
        byte[] byte_hex_code = CustomByteUtil.toByteArray(hex_code);
        Log.d(TAG, "下位机发送数据: " + byte_hex_code);
        sendSerialPort(byte_hex_code);
    }

    /**
     * 发送串口指令
     */
    public void sendSerialPort(byte[] sendData) {
//        Log.d(TAG, "sendSerialPort: 发送数据");
        try {
            if (sendData.length > 0) {
                mOutputStream.write(sendData);
                mOutputStream.write('\n');
                mOutputStream.write('\n');
                //outputStream.write('\r'+'\n');
                mOutputStream.flush();
                Log.d(TAG, "sendSerialPort: 串口数据发送成功 " + CustomByteUtil.toHexString(sendData));
            }
        } catch (IOException e) {
            Log.e(TAG, "sendSerialPort: 串口数据发送失败：" + e.toString());
        }
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                try {
//                    if (sendData.length > 0) {
//                        mOutputStream.write(sendData);
//                        mOutputStream.write('\n');
//                        //outputStream.write('\r'+'\n');
//                        mOutputStream.flush();
//                        Log.d(TAG, "sendSerialPort: 串口数据发送成功 " + CustomByteUtil.toHexString(sendData));
//                    }
//                } catch (IOException e) {
//                    Log.e(TAG, "sendSerialPort: 串口数据发送失败：" + e.toString());
//                }
//            }
//        }).start();
    }

    /**
     * 上位机下发设置信息
     */
    public void sendSetInfo(float temperature, float humidity, float ammonia, float hydrogen_sulfide, float illumination) {
        String s_temperature = CustomByteUtil.floatToHexString(temperature,100);
        String s_humidity = CustomByteUtil.floatToHexString(humidity,100);
        String s_ammonia = CustomByteUtil.floatToHexString(ammonia,1000);
        String s_hydrogen_sulfide = CustomByteUtil.floatToHexString(hydrogen_sulfide,1000);
        String s_illumination = CustomByteUtil.floatToHexString(illumination,1);
        String data = s_temperature + s_humidity + s_ammonia + s_hydrogen_sulfide + s_illumination;
//        Log.d(TAG, "上位机下发设置信息 --> " + data);
        sendCommand(FlagManager.SEND_SETTINGS, data);
    }
}
